/**
 * Copyright (c) 2013 onionframework.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.onionframework.event;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.commons.lang3.Validate;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
 * 事件监听器的管理者
 * 
 * 这里面初始化了所有的Onion事件监听器
 * 
 * @author Allan
 *
 */
@Component
public class SpringEventListenerRegistry implements BeanFactoryPostProcessor {

	/** 存放监听器的Map */
	private Map<String,Set<OnionEventListener>>  listeners = new HashMap<String,Set<OnionEventListener>>();
	
	private static final Pattern skipPackagesPattern 
		= Pattern.compile("^org\\.apache\\..*|^org\\.hibernate\\..*|^org\\.springframework\\..*");
	
	/**
	 * BeanFactoryPostProcessor接口定义的方法，该方法允许在spring实例化其它的bean之前读取配置元数据
	 */
	@Override
	public void postProcessBeanFactory(
			ConfigurableListableBeanFactory beanFactory) throws BeansException {
		// 获取所有spring管理的bean的注册名字
		String[] beans = beanFactory.getBeanDefinitionNames();
		Class<?> beanClass;
		Class<?> targetClass;
		Set<OnionEventListener> thisListeners;
		String[] eventKeys;
		Class<? extends ApplicationEvent>[] eventClasses;
		String classEventKey;
		// 遍历所有spring管理的bean
		for(String beanName : beans){
			
			if(skipPackagesPattern.matcher(beanFactory.getBeanDefinition(beanName).getBeanClassName()).matches())
				continue;
			
			try {
				beanClass = Class.forName(beanFactory.getBeanDefinition(beanName).getBeanClassName());
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			}
			
			targetClass = beanClass;
			// 循环遍历当前类的所有父类或者
			do {
				// 获取当前类的所有方法(公共的、私有的、重写的、静态的等等所有方法)，但是不包括继承的方法
				for (Method method : targetClass.getDeclaredMethods()) {
					// 判断方法上是否标注了com.ai.core.framework.event.Observer注解
					Observer annotation = method.getAnnotation(Observer.class);
					if (annotation != null 
							&& method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
						
						Assert.isTrue(annotation.value().length > 0 
								|| annotation.type().length > 0, 
								"事件监听器必须指明事件名称或者类型：" + method);

						eventKeys = annotation.value();
						
						for(String event : eventKeys){
							thisListeners = this.listeners.get(event);
							if(thisListeners == null){
								thisListeners = new LinkedHashSet<OnionEventListener>();
								this.listeners.put(event, thisListeners);
							}

							thisListeners.add(new OnionEventListener(beanName,method.getName(),method.getParameterTypes(),beanFactory));
						}
						
						eventClasses = annotation.type();
						
						for(Class<? extends ApplicationEvent> eventClass : eventClasses){
							classEventKey = SpringEventListener.getEventKey(eventClass);
							thisListeners = this.listeners.get(classEventKey);
							if(thisListeners == null){
								thisListeners = new LinkedHashSet<OnionEventListener>();
								this.listeners.put(classEventKey, thisListeners);
							}

							thisListeners.add(new OnionEventListener(beanName,method.getName(),method.getParameterTypes(),beanFactory));
						}
					}
				}
				targetClass = targetClass.getSuperclass();
			}
			while (targetClass != null && targetClass != Object.class);
		}
	}
	
	static class OnionEventListener{
		String bean;
		String method;
		Class<?>[] parameterTypes;
		BeanFactory beanFactory;
		
		public OnionEventListener(String bean, String method, Class<?>[] parameterTypes, BeanFactory beanFactory) {
			this.bean = bean;
			this.method = method;
			this.parameterTypes = parameterTypes;
			this.beanFactory = beanFactory;
		}
		
		public void onApplicationEvent(Object...parameters){
			//this.beanFactory.get
			invokeMethod(this.beanFactory.getBean(bean), method, parameterTypes, parameters);
		}
	}
	
	Set<OnionEventListener> getListeners(String eventKey){
		return this.listeners.get(eventKey);
	}
	
	private static Object invokeMethod(final Object obj,
			final String methodName, final Class<?>[] parameterTypes,
			final Object[] args) {
		Method method = getMethod(obj, methodName, parameterTypes);
		
		if (method == null) {
			throw new IllegalArgumentException("Could not find method ["
					+ methodName + "] on target [" + obj + "]");
		}

		try {
			return method.invoke(obj, args);
		} catch (Exception e) {
			if (e instanceof RuntimeException)
				throw (RuntimeException) e;
			else
				throw new RuntimeException(e);
		}
	}

	/**
	 * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. 如向上转型到Object仍无法找到, 返回null.
	 * 匹配函数名+参数类型。
	 * 
	 * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object...
	 * args)
	 */
	private static Method getMethod(final Object obj,
			final String methodName, final Class<?>... parameterTypes) {
		Validate.notNull(obj, "object can't be null");
		Validate.notBlank(methodName, "methodName can't be blank");

		for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType
				.getSuperclass()) {
			try {
				Method method = searchType.getDeclaredMethod(methodName,
						parameterTypes);
				return method;
			} catch (NoSuchMethodException e) {
				// Method不在当前类定义,继续向上转型
			}
		}
		return null;
	}
}
